(* List *)

type 'a list = 'a Lazy_list.list = LNil_ | LCons_ of 'a Lazy.t * 'a list Lazy.t
let ( @ ) = Lazy_list.append
let rec list_of_lazy_list l = match Lazy.force l with
  | LNil_ -> []
  | LCons_ (lazy x, rest) -> x :: list_of_lazy_list rest

(* Array *)

type 'a camlinternal_array_ = 'a array
type 'a array = 'a Lazy.t camlinternal_array_

(* Option *)

type 'a camlinternal_option_ = 'a option
type 'a option = 'a Lazy.t camlinternal_option_

(* Types *)

type out_channel = Pervasives.out_channel

(* Exceptions *)

exception Sys_error_ = Sys_error
exception Sys_error of string lazy_t
exception Failure_ = Failure
exception Failure of string lazy_t
exception Invalid_argument_ = Invalid_argument
exception Invalid_argument of string lazy_t
exception Match_failure_ = Match_failure
exception Match_failure of (string lazy_t * int lazy_t * int lazy_t) lazy_t
exception Assert_failure_ = Assert_failure
exception Assert_failure of (string lazy_t * int lazy_t * int lazy_t) lazy_t
exception Undefined_recursive_module_ = Undefined_recursive_module
exception Undefined_recursive_module of
  (string lazy_t * int lazy_t * int lazy_t) lazy_t

let camlinternal_rec_force_ (v : 'a) : 'a =
  let rec f o =
    let t = Obj.tag o in
      if t = Obj.lazy_tag then (
	f (Obj.repr (Lazy.force (Obj.obj o)))
      ) else if t = Obj.forward_tag then (
	Obj.set_field o 0 (f (Obj.field o 0));
	o
      ) else if t >= Obj.no_scan_tag then (
	o
      ) else (
	for i = 0 to Obj.size o - 1 do
	  Obj.set_field o i (f (Obj.field o i))
	done;
	o
      )
  in
    Obj.obj (f (Obj.repr v))

let camlinternal_export_exn_ exn =
  match Lazy.force exn with
    | Sys_error (lazy msg) ->
	Sys_error_ msg
    | Failure (lazy msg) ->
	Failure_ msg
    | Invalid_argument (lazy msg) ->
	Invalid_argument_ msg
    | Match_failure (lazy ((lazy f), (lazy i), (lazy j))) ->
	Match_failure_ (f, i, j)
    | Assert_failure (lazy ((lazy f), (lazy i), (lazy j))) ->
	Assert_failure_ (f, i, j)
    | Undefined_recursive_module (lazy ((lazy f), (lazy i), (lazy j))) ->
	Undefined_recursive_module_ (f, i, j)
    | exn ->
	camlinternal_rec_force_ exn

let camlinternal_import_exn_ exn =
  match exn with
    | Sys_error_ msg ->
	Sys_error (lazy msg)
    | Failure_ msg ->
	Failure (lazy msg)
    | Invalid_argument_ msg ->
	Invalid_argument (lazy msg)
    | Match_failure_ (f, i, j) ->
	Match_failure (lazy (lazy f, lazy i, lazy j))
    | Undefined_recursive_module_ (f, i, j) ->
	Undefined_recursive_module (lazy (lazy f, lazy i, lazy j))
    | exn ->
	exn

let raise = lazy (fun exn -> raise (camlinternal_export_exn_ exn))
let failwith = lazy (fun s -> (Lazy.force raise) (lazy (Failure s)))
let invalid_arg = lazy (fun s -> (Lazy.force raise) (lazy (Invalid_argument s)))

(* Compare *)

let lazy_forward_ (o : 'a) : 'a =
  let rec f v =
    let t = Obj.tag v in
      if t = Obj.forward_tag then f (Obj.obj (Obj.field v 0))
      else if t = Obj.lazy_tag then f (Lazy.force (Obj.obj v))
      else (Obj.obj v)
  in
    f (Obj.repr o)

let lazy_compare_aux_ (v1 : 'a) (v2 : 'a) (total : bool) : int =
  let less = -1 in
  let equal = 0 in
  let greater = 1 in
  let unordered = -2 in
  let compare_int v1 v2 =
    if (Obj.obj v1 : int) < (Obj.obj v2 : int) then less
    else if (Obj.obj v1 : int) > (Obj.obj v2 : int) then greater
    else equal
  in
  let rec f v1 v2 = 
    let t1 = Obj.tag v1 and t2 = Obj.tag v2 in
    let v1 = lazy_forward_ v1 in
    let v2 = lazy_forward_ v2 in
    if v1 == v2 && total then equal
    else if Obj.is_int v1 then
      if v1 == v2 then equal
      else if Obj.is_int v2 then compare_int v1 v2
      else less
    else if Obj.is_int v2 then greater
    else if t1 = Obj.out_of_heap_tag && t2 = Obj.out_of_heap_tag then
      compare_int v1 v2
    else
      if t1 <> t2 then compare_int (Obj.repr t1) (Obj.repr t2)
      else
	if t1 = Obj.string_tag then
	  Pervasives.compare (Obj.obj v1 : string) (Obj.obj v2 : string)
	else if t1 = Obj.double_tag then
	  let o1 : float = Obj.obj v1 in
	  let o2 : float = Obj.obj v2 in
	  let c = Pervasives.compare o1 o2 in
	    if total || o1 = o2 then c else unordered
	else if t1 = Obj.double_array_tag then
	  let o1 : float array = Obj.obj v1 in
	  let o2 : float array = Obj.obj v2 in
	  let c = Pervasives.compare o1 o2 in
	    if total || o1 = o2 then c else unordered
	else if t1 = Obj.abstract_tag then
	  Pervasives.invalid_arg "equal: abstract value"
	else if t1 = Obj.closure_tag || t1 = Obj.infix_tag then
	  Pervasives.invalid_arg "equal: functionnal value"
	else if t1 = Obj.object_tag || t1 = Obj.custom_tag then
	  Pervasives.compare (Obj.obj v1) (Obj.obj v2)
	else
	  let sz1 = Obj.size v1 and sz2 = Obj.size v2 in
	    if sz1 < sz2 then less
	    else if sz1 > sz2 then greater
	    else
	      let rec g i =
		if i = sz1 then equal
		else
		  let c = f (Obj.field v1 i) (Obj.field v2 i) in
		    if c = equal then g (succ i)
		    else c
	      in
		g 0
  in
    f (Obj.repr (Lazy.force v1)) (Obj.repr (Lazy.force v2))
;;

let compare = lazy (fun x y ->
		      let r = lazy_compare_aux_ x y true in
			if r = 0 then 0 else if r = 1 then 1 else -1)

(* Comparisons *)

let ( <> ) = lazy (fun x y -> lazy_compare_aux_ x y false <> 0)
let ( < ) = lazy (fun x y -> lazy_compare_aux_ x y false = -1)
let ( > ) = lazy (fun x y -> lazy_compare_aux_ x y false = 1)
let ( <= ) = lazy (fun x y -> let r = lazy_compare_aux_ x y false in
		     r = 0 || r = -1)
let ( >= ) = lazy (fun x y -> lazy_compare_aux_ x y false >= 0)
let ( = ) = lazy (fun x y -> lazy_compare_aux_ x y false = 0)

let min = lazy (fun x y -> if Pervasives.(<) (lazy_compare_aux_ x y false) 0
		then Lazy.force x else Lazy.force y)
let max = lazy (fun x y -> if Pervasives.(>) (lazy_compare_aux_ x y false) 0
		then Lazy.force x else Lazy.force y)

let ( == ) =
  lazy (fun x y -> lazy_forward_ (Lazy.force x) == lazy_forward_ (Lazy.force y))
let ( != ) =
  lazy (fun x y -> lazy_forward_ (Lazy.force x) != lazy_forward_ (Lazy.force y))

(* Boolean operations *)

let not = lazy (fun x -> not (Lazy.force x))
let ( & ) = lazy (fun x y -> (Lazy.force x) & (Lazy.force y))
let ( && ) = lazy (fun x y -> (Lazy.force x) && (Lazy.force y))
let ( or ) = lazy (fun x y -> (Lazy.force x) or (Lazy.force y))
let ( || ) = lazy (fun x y -> (Lazy.force x) || (Lazy.force y))

(* Integer operations *)

let ( ~- ) = lazy (fun x -> ~- (Lazy.force x))
(*let ( ~+ ) = lazy (fun x -> ~+ (Lazy.force x))*)
let succ = lazy (fun x -> succ (Lazy.force x))
let pred = lazy (fun x -> pred (Lazy.force x))
let ( + ) = lazy (fun x y -> (Lazy.force x) + (Lazy.force y))
let ( - ) = lazy (fun x y -> (Lazy.force x) - (Lazy.force y))
let ( * ) = lazy (fun x y -> (Lazy.force x) * (Lazy.force y))
let ( / ) = lazy (fun x y -> (Lazy.force x) / (Lazy.force y))
let ( mod ) = lazy (fun x y -> (Lazy.force x) mod (Lazy.force y))

let abs = lazy (fun x -> abs (Lazy.force x))

let ( land ) = lazy (fun x y -> (Lazy.force x) land (Lazy.force y))
let ( lor ) = lazy (fun x y -> (Lazy.force x) lor (Lazy.force y))
let ( lxor ) = lazy (fun x y -> (Lazy.force x) lxor (Lazy.force y))

let lnot = lazy (fun x -> lnot (Lazy.force x))

let ( lsl ) = lazy (fun x y -> (Lazy.force x) lsl (Lazy.force y))
let ( lsr ) = lazy (fun x y -> (Lazy.force x) lsr (Lazy.force y))
let ( asr ) = lazy (fun x y -> (Lazy.force x) asr (Lazy.force y))

let min_int = lazy min_int
let max_int = lazy max_int

(* Floating-point operations *)

let ( ~-. ) = lazy (fun x -> ~-. (Lazy.force x))
(*let ( ~+. ) = lazy (fun x -> ~+. (Lazy.force x))*)
let ( +. ) = lazy (fun x y -> (Lazy.force x) +. (Lazy.force y))
let ( -. ) = lazy (fun x y -> (Lazy.force x) -. (Lazy.force y))
let ( *. ) = lazy (fun x y -> (Lazy.force x) *. (Lazy.force y))
let ( /. ) = lazy (fun x y -> (Lazy.force x) /. (Lazy.force y))
let ( ** ) = lazy (fun x y -> (Lazy.force x) ** (Lazy.force y))

let exp = lazy (fun x -> exp (Lazy.force x))
(*let expm1 = lazy (fun x -> exmp1 (Lazy.force x))*)
let acos = lazy (fun x -> acos (Lazy.force x))
let asin = lazy (fun x -> asin (Lazy.force x))
let atan = lazy (fun x -> atan (Lazy.force x))
let atan2 = lazy (fun x y -> atan2 (Lazy.force x) (Lazy.force y))
let cos = lazy (fun x -> cos (Lazy.force x))
let cosh = lazy (fun x -> cosh (Lazy.force x))
let log = lazy (fun x -> log (Lazy.force x))
let log10 = lazy (fun x -> log10 (Lazy.force x))
(*let log1p = lazy (fun x -> log1p (Lazy.force x))*)
let sin = lazy (fun x -> sin (Lazy.force x))
let sinh = lazy (fun x -> sinh (Lazy.force x))
let sqrt = lazy (fun x -> sqrt (Lazy.force x))
let tan = lazy (fun x -> tan (Lazy.force x))
let tanh = lazy (fun x -> tanh (Lazy.force x))
let ceil = lazy (fun x -> ceil (Lazy.force x))
let floor = lazy (fun x -> floor (Lazy.force x))
let abs_float = lazy (fun x -> abs_float (Lazy.force x))
let mod_float = lazy (fun x y -> mod_float (Lazy.force x) (Lazy.force y))
let frexp = lazy (fun x -> frexp (Lazy.force x))
let ldexp = lazy (fun x y -> ldexp (Lazy.force x) (Lazy.force y))
let modf = lazy (fun x -> modf (Lazy.force x))
let float = lazy (fun x -> float (Lazy.force x))
let float_of_int = lazy (fun x -> float_of_int (Lazy.force x))
let truncate = lazy (fun x -> truncate (Lazy.force x))
let int_of_float = lazy (fun x -> int_of_float (Lazy.force x))

let infinity = lazy infinity
let neg_infinity = lazy neg_infinity
let nan = lazy nan
let max_float = lazy max_float
let min_float = lazy min_float
let epsilon_float = lazy epsilon_float

let classify_float = lazy (fun x -> classify_float (Lazy.force x))

(* String operations -- more in module String *)

let ( ^ ) = lazy (fun x y -> (Lazy.force x) ^ (Lazy.force y))

(* Character operations -- more in module Char *)

let int_of_char = lazy (fun x -> int_of_char (Lazy.force x))
let char_of_int = lazy (fun x -> char_of_int (Lazy.force x))

(* Unit operations *)

let ignore = lazy (fun x -> ignore (Lazy.force x))

(* Pair operations *)

let fst = lazy (fun x -> Lazy.force (fst (Lazy.force x)))
let snd = lazy (fun x -> Lazy.force (snd (Lazy.force x)))

(* String conversion functions *)

let string_of_bool = lazy (fun x -> string_of_bool (Lazy.force x))
let bool_of_string = lazy (fun x -> bool_of_string (Lazy.force x))
let string_of_int = lazy (fun x -> string_of_int (Lazy.force x))
let int_of_string = lazy (fun x -> int_of_string (Lazy.force x))
let string_of_float = lazy (fun x -> string_of_float (Lazy.force x))
let float_of_string = lazy (fun x -> float_of_string (Lazy.force x))

(* I/O operations *)

let stdin = lazy stdin
let stdout = lazy stdout
let stderr = lazy stderr

(* General output functions *)

let open_out_gen =
  lazy (fun x y z ->
	  open_out_gen (list_of_lazy_list x) (Lazy.force y) (Lazy.force z))
let open_out = lazy (fun x -> open_out (Lazy.force x))
let open_out_bin = lazy (fun x -> open_out_bin (Lazy.force x))

let flush = lazy (fun x -> flush (Lazy.force x))
let flush_all = lazy (fun x -> flush_all (Lazy.force x))

let output_char =
  lazy (fun x y -> output_char (Lazy.force x) (Lazy.force y))
let output_string =
  lazy (fun x y -> output_string (Lazy.force x) (Lazy.force y))
let output =
  lazy (fun x y z t -> output (Lazy.force x) (Lazy.force y) (Lazy.force z)
	  (Lazy.force t))
let output_byte =
  lazy (fun x y -> output_byte (Lazy.force x) (Lazy.force y))
let output_binary_int =
  lazy (fun x y -> output_binary_int (Lazy.force x) (Lazy.force y))
let output_value =
 lazy (fun x y -> output_value (Lazy.force x) (Lazy.force y))

let seek_out =
  lazy (fun (lazy x) (lazy y) -> seek_out (Lazy.force x) (Lazy.force y))
let pos_out =
  lazy (fun x -> pos_out (Lazy.force x))
let out_channel_length =
  lazy (fun x -> out_channel_length (Lazy.force x))
(*let close_out_channel =
  lazy (fun x -> close_out_channel (Lazy.force x))*)
let close_out =
  lazy (fun x -> close_out (Lazy.force x))
let close_out_noerr =
  lazy (fun x -> close_out_noerr (Lazy.force x))
let set_binary_mode_out =
  lazy (fun x -> set_binary_mode_out (Lazy.force x))

let open_in_gen =
  lazy (fun x y z ->
	  open_in_gen (list_of_lazy_list x) (Lazy.force y) (Lazy.force z))
let open_in =
  lazy (fun x -> open_in (Lazy.force x))
let open_in_bin =
  lazy (fun x -> open_in_bin (Lazy.force x))

let input_char =
  lazy (fun x -> input_char (Lazy.force x))

let input =
  lazy (fun x y z t -> input (Lazy.force x) (Lazy.force y) (Lazy.force z)
	  (Lazy.force t))
let really_input =
  lazy (fun x y z t -> really_input (Lazy.force x) (Lazy.force y) (Lazy.force z)
	  (Lazy.force t))
(*let input_scan_line =
  lazy (fun x -> input_scan_line (Lazy.force x))*)
let input_line =
  lazy (fun x -> input_line (Lazy.force x))

let input_byte =
  lazy (fun x -> input_byte (Lazy.force x))
let input_binary_int =
  lazy (fun x -> input_binary_int (Lazy.force x))
let input_value =
  lazy (fun x -> input_value (Lazy.force x))
let seek_in =
  lazy (fun x y -> seek_in (Lazy.force x) (Lazy.force y))
let pos_in =
  lazy (fun x -> pos_in (Lazy.force x))
let in_channel_length =
  lazy (fun x -> in_channel_length (Lazy.force x))
let close_in =
  lazy (fun x -> close_in (Lazy.force x))
let close_in_noerr =
  lazy (fun x -> close_in_noerr (Lazy.force x))
let set_binary_mode_in =
  lazy (fun x y -> set_binary_mode_in (Lazy.force x) (Lazy.force y))

(* Output functions on standard output *)

let print_char = lazy (fun x -> print_char (Lazy.force x))
let print_string = lazy (fun x -> print_string (Lazy.force x))
let print_int = lazy (fun x -> print_int (Lazy.force x))
let print_float = lazy (fun x -> print_float (Lazy.force x))
let print_endline = lazy (fun x -> print_endline (Lazy.force x))
let print_newline = lazy (fun x -> print_newline (Lazy.force x))

(* Output functions on standard error *)

let prerr_char = lazy (fun x -> prerr_char (Lazy.force x))
let prerr_string = lazy (fun x -> prerr_string (Lazy.force x))
let prerr_int = lazy (fun x -> prerr_int (Lazy.force x))
let prerr_float = lazy (fun x -> prerr_float (Lazy.force x))
let prerr_endline = lazy (fun x -> prerr_endline (Lazy.force x))
let prerr_newline = lazy (fun x -> prerr_newline (Lazy.force x))

(* Input functions on standard input *)

let read_line = lazy (fun x -> read_line (Lazy.force x))
let read_int = lazy (fun x -> read_int (Lazy.force x))
let read_float = lazy (fun x -> read_float (Lazy.force x))

(* Operations on large files *)

module LargeFile =
  struct
    let seek_out =
      lazy (fun x y -> LargeFile.seek_out (Lazy.force x) (Lazy.force y))
    let pos_out =
      lazy (fun x -> LargeFile.pos_out (Lazy.force x))
    let out_channel_length =
      lazy (fun x -> LargeFile.out_channel_length (Lazy.force x))
    let seek_in =
      lazy (fun x y -> LargeFile.seek_in (Lazy.force x) (Lazy.force y))
    let pos_in =
      lazy (fun x -> LargeFile.pos_in (Lazy.force x))
    let in_channel_length =
      lazy (fun x -> LargeFile.in_channel_length (Lazy.force x))
  end

(* References *)

let ref = lazy (fun x -> ref (Lazy.force x))
let ( ! ) = lazy (fun x -> (!) (Lazy.force x))
let ( := ) = lazy (fun x y -> ( := ) (Lazy.force x) (Lazy.force y))
let incr = lazy (fun x -> incr (Lazy.force x))
let decr = lazy (fun x -> decr (Lazy.force x))

(* Formats *)

let format_of_string = lazy Lazy.force
(*let format_to_string = lazy (fun x -> format_to_string (Lazy.force x))*)
(*let string_to_format = lazy (fun x -> string_to_format (Lazy.force x))*)

(*let ( ^^ ) = lazy (fun x y -> ( ^^ ) (Lazy.force x) (Lazy.force y))*)

let string_of_format = lazy Lazy.force

(* Miscellaneous *)

let at_exit = lazy (fun f -> at_exit (fun () -> (Lazy.force f) (lazy ())))
let do_at_exit = lazy (fun x -> do_at_exit (Lazy.force x))

let exit = lazy (fun x -> exit (Lazy.force x))
